#!/bin/bash
 
###########################################
#                                         #
# This file is part of Darkone Firewall.  #
#                                         #
###########################################

#############################################################################
#                                                                           #                                                                     
# Darkone Firewall is free software: you can redistribute it and/or modify  #
# it under the terms of the GNU General Public License as published by      #
# the Free Software Foundation, either version 3 of the License, or         #
# (at your option) any later version.                                       #
#                                                                           #
# Darkone Firewall is distributed in the hope that it will be useful,       #
# but WITHOUT ANY WARRANTY; without even the implied warranty of            #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             #
# GNU General Public License for more details.                              #
#                                                                           #
# You should have received a copy of the GNU General Public License         #
# along with this program.  If not, see <http://www.gnu.org/licenses/>.     #
#                                                                           #
#############################################################################

nat_check_chain ()
{
	local CHAIN="$1"
	local VALID_CHAIN=0
        for CH in OUTPUT POSTROUTING PREROUTING; do

                if [[ "$CHAIN" == "$CH" ]]; then

                        VALID_CHAIN=1

                fi

        done
        if [[ "$VALID_CHAIN" -ne 1 ]]; then

                echo
                echo "Error: No valid chain ($CHAIN) found at line $LINE_NR. Exiting ..."
                echo
                exit 5

        fi
}

nat_check_access ()
{
	local THE_RULE="$1"
	local RULE_NR="`echo "$THE_RULE" | awk -F ';' '{print NF}'`"

	if [[ "$RULE_NR" -gt 1 ]]; then
		
		echo
		echo " Error: Multiple destination ports at line $LINE_NR is not supported in nat module! Exiting ..."
		echo
		exit 26
	fi

        VALID_RULE="`echo "$THE_RULE" | grep "[a-z]\{1,\}[|][0-9]\{1,\}" | grep -v ",$" | grep -v ";$"`"
        VALID_RULE_ANY="`echo "$THE_RULE" | grep "any" | grep -v ",$" | grep -v ";$"`"
        if [[ -z "$VALID_RULE" ]] && [[ "$VALID_RULE_ANY" != "any" ]]; then

                echo
                echo "Error: Invalid rule number 1 at line $LINE_NR. Exiting ..."
                echo
                exit 7
        fi
}

nat_check_target ()
{
	local _TARGET="$1"
	local TARGET="`echo "$_TARGET" | awk -F '|' '{print $1}'`"
	local VALID_TARGET=0

	for trg in SNAT DNAT MASQUERADE; do
	
	
		if [[ "$trg" == "$TARGET" ]] && [[ "$TARGET" == "SNAT" ]]; then

			IP="`echo "$_TARGET" | awk -F '|' '{print $2}' | egrep "\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b"`"
			if [[ -z "$IP" ]]; then
		
				echo
				echo "Error: Invalid IP address in SNAT rule at line $LINE_NR. Exiting ..."
				echo
				exit 13
			fi
			VALID_TARGET=1

	
		elif [[ "$trg" == "$TARGET" ]] && [[ "$TARGET" == "DNAT" ]]; then

			IP="`echo "$_TARGET" | awk -F '|' '{print $2}' | egrep "\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b"`"
	                if [[ -z "$IP" ]]; then 
	
	                        echo
	                        echo "Error: Invalid IP address in DNAT rule at line $LINE_NR. Exiting ..."
	                        echo
	                        exit 14
	                fi
			PORT="`echo "$_TARGET" | awk -F '|' '{print $3}'`"
			if [[ "$PORT" -gt 65534 ]] || [[ "$PORT" -lt 1 ]]; then

                                echo
                                echo "Error: Invalid destination port number in DNAT rule at line $LINE_NR. Exiting ..."
                                echo
                                exit 24
                        fi

			VALID_TARGET=1

		elif [[ "$trg" == "$TARGET" ]]; then

				VALID_TARGET=1
		fi
	done

	if [[ "$VALID_TARGET" -ne 1 ]]; then
		
		echo
                echo "Error: No valid target at line $LINE_NR. Exiting ..."
                echo
                exit 15

	fi
}

nat_check_rule_fields () 
{
	local rule="$1"
	LINE_NR="`grep -n "$rule" $CONF_FILE | awk -F : '{print $1}'`"
	NR_FIELDS="`echo "$rule" | awk -F : '{print NF}'`"

	if [[ "$NR_FIELDS" -lt 5 ]]; then
		
		echo
		echo "Error: Number of fields is less then 5 at line $LINE_NR. Exiting ..."
		echo
		exit 1

	fi
	
	get_parameters "$rule"
	
	check_ip_src "$IP_SRC"
	check_ip_dst "$IP_DST"
	check_interface "$INTERFACE"
	nat_check_chain "$CHAIN"
	nat_check_access "$ACCESS"
	nat_check_target "$TARGET"

	nat_do_some_checks "$IP_SRC" "$IP_DST" "$INTERFACE" "$CHAIN" "$ACCESS" "$TARGET"

}

nat_check_config ()
{
	
	CONF_FILE="$1"
	local base_file_name="`basename $CONF_FILE`"
	local module="$2"

	if [[ ! -s "$CONF_FILE" ]]; then
	
		echo
		echo "Error: Rules file ($CONF_FILE) doesn't exist or exist and is empty! Exiting ..."
		echo
		stop
		exit 8

	fi
	
	make_tmp_rule_file "$CONF_FILE" "$base_file_name" "$module"

	exec 3<> ${TMP_DIR}/${module}_${base_file_name}	
	while read rule_line <&3; do
		
		nat_check_rule_fields "$rule_line"
		error_no="$?"
		if [[ "$error_no" -ne 0 ]]; then
			
			exit $error_no
		fi
	done
	exec 3>&-

}

nat_do_some_checks ()
{
	
	 local IP_SRC="$1"
	 local IP_DST="$2"
         local INTERFACE="$3"
	 local CHAIN="$4"
	 local ACCESS="$5"
	 local TARGET="`echo "$6" | awk -F '|' '{print $1}'`"

	 if [[ "$INTERFACE" == "any" ]] && [[ "$ACCESS" == "any" ]]; then

                        echo
                        echo "Error: Interface and Rules can't be any in the same time at line $LINE_NR! Exiting ..."
                        echo
                        exit 10
         fi

         if [[ "$TARGET" == "MASQUERADE" ]] && [[ "$CHAIN" != "POSTROUTING" ]]; then

	                echo
                        echo "Error: MASQUERADE target can only be used in POSTROUTING chain at line $LINE_NR! Exiting ..."
                        echo
                        exit 16
         fi

         if [[ "$TARGET" == "SNAT" ]] && [[ "$CHAIN" != "POSTROUTING" ]]; then

                        echo
                        echo "Error: SNAT target can only be used in POSTROUTING chain at line $LINE_NR! Exiting ..."
                        echo
                        exit 17
         fi

	 if [[ "$INTERFACE" == "any" ]] && [[ "$CHAIN" == "POSTROUTING" ]]; then

                        echo
                        echo "Error: When using POSTROUTING chain you must specify an interface at line $LINE_NR! Exiting ..."
                        echo
                        exit 18
         fi

	 if [[ "$TARGET" == "DNAT" ]] && [[ "$CHAIN" == "POSTROUTING" ]]; then

                        echo
                        echo "Error: DNAT target can't be used in POSTROUTING chain at line $LINE_NR! Exiting ..."
                        echo
                        exit 25
         fi

	 if [[ "$CHAIN" == "POSTROUTING" ]] && [[ "$ACCESS" != "any" ]]; then

			echo
			echo " Error: This version only supports \"any\" access rule at line $LINE_NR! Exiting ..."
			echo
			exit 27
	 fi

}
nat_apply_iptables_rule ()
{

	local IP_SRC="$1"
        local IP_DST="$2"
        local INTERFACE="$3"
        local CHAIN="$4"
        local ACCESS="$5"
        local TARGET="`echo "$6" | awk -F '|' '{print $1}'`"
	local ACCESS_NR="$7"
	local FILTER_NAME="$8"
	local module="${9}:${FILTER_NAME}"

	# echo $IP_SRC $IP_DST $INTERFACE $CHAIN $ACCESS $TARGET $ACCESS_NR $FILTER_NAME $module

	if [[ "$TARGET" == "DNAT" ]]; then
		local DNAT_IP_DST="`echo "$6" | awk -F '|' '{print $2}'`"
		local DNAT_PORT_DST="`echo "$6" | awk -F '|' '{print $3}'`"
	fi
	
	if [[ "$TARGET" == "SNAT" ]]; then
                local SNAT_IP_SRC="`echo "$6" | awk -F '|' '{print $2}'`"
        fi

	local PROTO="`echo "$ACCESS" | awk -F '|' '{print $1}'`"
        local PORT="`echo "$ACCESS" | awk -F '|' '{print $2}'`"
	
	if [[ "$INTERFACE" == "any" ]]; then
		
		if [ "$PROTO" == tcp ]; then
                	$IPT -t nat -A $CHAIN -p "$PROTO" -s "$IP_SRC" -d "$IP_DST" -m comment --comment "$module" -m tcp --dport $PORT -j "$TARGET" --to-destination ${DNAT_IP_DST}:${DNAT_PORT_DST}
                elif [ "$PROTO" == udp ]; then
                        $IPT -t nat -A $CHAIN -p "$PROTO" -s "$IP_SRC" -d "$IP_DST" -m comment --comment "$module" -m udp --dport $PORT -j "$TARGET" --to-destination ${DNAT_IP_DST}:${DNAT_PORT_DST}
                else
                        echo "Warning: No protocol match at line $LINE_NR ... we will continue!!!"
                fi

	elif [[ "$PROTO" == "any" ]] && [[ "$CHAIN" == "POSTROUTING" ]]; then

		if [[ "$TARGET"  == "MASQUERADE" ]]; then

                        $IPT -t nat -A $CHAIN -o "$INTERFACE" -s "$IP_SRC" -d "$IP_DST" -m comment --comment "$module" -j "$TARGET"

                elif [[ "$TARGET"  == "SNAT" ]]; then

                        $IPT -t nat -A $CHAIN -o "$INTERFACE" -s "$IP_SRC" -d "$IP_DST" -m comment --comment "$module" -j "$TARGET" --to-source "$SNAT_IP_SRC"
                fi
			
	fi

}

nat_apply_rules ()
{
	local TMP_CONF_FILE="$1"
	local CONF_FILE="$2"
	local module="$3"
	

	exec 3<> $TMP_CONF_FILE
	while read rule_line <&3; do

		LINE_NR="`grep -n "$rule_line" $CONF_FILE | awk -F : '{print $1}'`"
		get_parameters "$rule_line"
	
		if [[ "$IP_SRC" == "any" ]]; then
                        IP_SRC="0.0.0.0/0"
                fi
                if [[ "$IP_DST" == "any" ]]; then
                        IP_DST="0.0.0.0/0"
                fi

		FILTER_NAME="`basename $CONF_FILE | awk -F . '{print $1}'`"
		nat_apply_iptables_rule "$IP_SRC" "$IP_DST" "$INTERFACE" "$CHAIN" "$ACCESS" "$TARGET" "$ACCESS_NR" "$FILTER_NAME" "$module"
		echo "$IP_SRC $IP_DST $INTERFACE $CHAIN $ACCESS $TARGET"
		
	done
	exec 3>&-
	
}


nat ()
{

	local CONF_FILES="`ls $CONF_DIR |  grep "[.]conf$" `"
	local module="nat"
        
	for file in $CONF_FILES; do

                nat_check_config "${CONF_DIR}/${file}" "$module"
        done

        for file in $CONF_FILES; do

                nat_apply_rules "${TMP_DIR}/${module}_${file}" "${CONF_DIR}/${file}" "$module"
        done

}
